/**
 * @(#)tokenUtils.java, 8月 12, 2021.
 * <p>
 * Copyright 2021.
 */
package com.framework.util;

import com.auth0.jwt.JWT;
import com.auth0.jwt.JWTCreator;
import com.auth0.jwt.JWTVerifier;
import com.auth0.jwt.algorithms.Algorithm;
import com.auth0.jwt.interfaces.Claim;
import com.auth0.jwt.interfaces.DecodedJWT;
import com.framework.common.config.AudienceConf;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;

import javax.servlet.http.HttpServletRequest;
import java.util.Calendar;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


@Component
public class JwtUtil {

    private static final Logger log = LoggerFactory.getLogger(JwtUtil.class);

    public static final String AUTH_HEADER_KEY = "Authorization";

    public static final String TOKEN_PREFIX = "Bearer ";

    @Autowired
    private AudienceConf audience;

    @Value("${spring.application.name}")
    private String applicationName;

    public Calendar buildExpireTimeCalendar() {
        AudienceConf.Config audience = AudienceConf.buildAudience(this.audience, applicationName);
        Calendar nowTime = Calendar.getInstance();
        if (audience.getExpireType() == Calendar.YEAR) {
            nowTime.add(Calendar.YEAR, audience.getExpireTime());
        }
        if (audience.getExpireType() == Calendar.MONTH) {
            nowTime.add(Calendar.MONTH, audience.getExpireTime());
        }
        if (audience.getExpireType() == Calendar.DAY_OF_YEAR) {
            nowTime.add(Calendar.DAY_OF_YEAR, audience.getExpireTime());
        }
        if (audience.getExpireType() == Calendar.HOUR) {
            nowTime.add(Calendar.HOUR, audience.getExpireTime());
        }
        if (audience.getExpireType() == Calendar.MINUTE) {
            nowTime.add(Calendar.MINUTE, audience.getExpireTime());
        }
        if (audience.getExpireType() == Calendar.SECOND) {
            nowTime.add(Calendar.SECOND, audience.getExpireTime());
        }
        return nowTime;
    }

    public String buildToken(Map<String, String> claims) {
        AudienceConf.Config audience = AudienceConf.buildAudience(this.audience, applicationName);
        Calendar nowTime = buildExpireTimeCalendar();
        Map<String, Object> map = new HashMap<>();
        map.put("alg", "HS256");
        map.put("typ", "JWT");
        JWTCreator.Builder builder = JWT.create();
        builder.withHeader(map);
        for (Map.Entry<String, String> entry : claims.entrySet()) {
            builder.withClaim(entry.getKey(), entry.getValue());
        }
        builder.withExpiresAt(nowTime.getTime());
        return builder.sign(Algorithm.HMAC256(audience.getSecret()));
    }

    public Map<String, String> verifyToken2String(String token) {
        AudienceConf.Config audience = AudienceConf.buildAudience(this.audience, applicationName);
        JWTVerifier verifier = JWT.require(Algorithm.HMAC256(audience.getSecret())).build();
        DecodedJWT jwt = null;

        try {
            jwt = verifier.verify(token);
        } catch (Exception e) {
            log.error("verifyToken2String failed", e);
            return Collections.emptyMap();
        }

        Map<String, Claim> claims = jwt.getClaims();
        Map<String, String> result = new HashMap<>();
        for (Map.Entry<String, Claim> entry : claims.entrySet()) {
            result.put(entry.getKey(), entry.getValue().asString());
        }
        return result;
    }

    public String getClaimValue(String token, String key) {
        Map<String, String> result = verifyToken2String(token);
        if (CollectionUtils.isEmpty(result)) {
            return null;
        }
        return result.get(key);
    }

    /**
     * 获取token
     *
     * @param request
     * @return
     */
    public static String getToken(HttpServletRequest request) {
        final String authHeader = request.getHeader(AUTH_HEADER_KEY);
        log.info("## authHeader= {}", authHeader);
        if (StringUtils.isBlank(authHeader) || !authHeader.startsWith(TOKEN_PREFIX)) {
            return null;
        }
        return authHeader.substring(7);
    }

    public static String getToken(HttpHeaders httpHeaders) {
        List<String> list = httpHeaders.get(AUTH_HEADER_KEY);
        final String authHeader = CollectionUtils.isEmpty(list) ? null : list.get(0);
        log.info("## authHeader= {}", authHeader);
        if (StringUtils.isBlank(authHeader) || !authHeader.startsWith(TOKEN_PREFIX)) {
            return null;
        }
        return authHeader.substring(7);
    }
}
